package com.stx.jeepay.payment.controller.refund;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.stx.jeepay.core.dao.RefundOrderDao;
import com.stx.jeepay.core.entity.MchAppEntity;
import com.stx.jeepay.core.entity.MchInfoEntity;
import com.stx.jeepay.core.entity.PayOrderEntity;
import com.stx.jeepay.core.entity.RefundOrderEntity;
import com.stx.jeepay.core.expection.BizException;
import com.stx.jeepay.core.model.ApiRes;
import com.stx.jeepay.core.service.PayOrderService;
import com.stx.jeepay.core.service.RefundOrderService;
import com.stx.jeepay.core.utils.SeqKit;
import com.stx.jeepay.core.utils.SpringBeansUtil;
import com.stx.jeepay.core.utils.StringKit;
import com.stx.jeepay.payment.channel.IRefundService;
import com.stx.jeepay.payment.controller.ApiController;
import com.stx.jeepay.payment.exception.ChannelException;
import com.stx.jeepay.payment.model.MchAppConfigContext;
import com.stx.jeepay.payment.rqrs.msg.ChannelRetMsg;
import com.stx.jeepay.payment.rqrs.refund.RefundOrderRQ;
import com.stx.jeepay.payment.rqrs.refund.RefundOrderRS;
import com.stx.jeepay.payment.service.ConfigContextQueryService;
import com.stx.jeepay.payment.service.PayMchNotifyService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * 商户发起退款 controller
 */
@Slf4j
@RestController
public class RefundOrderController extends ApiController {
    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private RefundOrderService refundOrderService;
    @Autowired
    private PayMchNotifyService payMchNotifyService;
    @Autowired
    private ConfigContextQueryService configContextQueryService;
    @Autowired
    private RefundOrderDao refundOrderDao;

    /**
     * 申请退款
     **/
    @PostMapping("/api/refund/refundOrder")
    public ApiRes refundOrder() {
        RefundOrderEntity refundOrder = null;

        // 获取参数
        RefundOrderRQ rq = getRQByWithMchSign(RefundOrderRQ.class);

        try {
            if (StringUtils.isAllEmpty(rq.getMchOrderNo(), rq.getPayOrderId())) {
                throw new BizException("mchOrderNo 和 payOrderId不能同时为空");
            }

            if (StringUtils.isNotEmpty(rq.getNotifyUrl()) && !StringKit.isAvailableUrl(rq.getNotifyUrl())) {
                throw new BizException("异步通知地址协议仅支持http:// 或 https:// !");
            }

            PayOrderEntity payOrder = payOrderService.queryMchOrder(rq.getMchNo(), rq.getPayOrderId(), rq.getMchOrderNo());
            if (payOrder == null) {
                throw new BizException("退款订单不存在");
            }

            if (payOrder.getState() != PayOrderEntity.STATE_SUCCESS) {
                throw new BizException("订单状态不正确，无法进行退款");
            }

            if (payOrder.getRefundState() == PayOrderEntity.REFUND_STATE_ALL || payOrder.getRefundAmount() >= payOrder.getAmount()) {
                throw new BizException("订单已全额退款，本次申请失败");
            }

            if (payOrder.getRefundAmount() + rq.getRefundAmount() > payOrder.getAmount()) {
                throw new BizException("申请金额超出订单可退款余额，请检查退款金额");
            }

            if (refundOrderService.count(new LambdaQueryWrapper<RefundOrderEntity>()
                    .eq(RefundOrderEntity::getPayOrderId, payOrder.getPayOrderId())
                    .eq(RefundOrderEntity::getState, RefundOrderEntity.STATE_ING)) > 0) {
                throw new BizException("支付订单具有在途退款申请，请稍后再试");
            }

            //全部退款金额 （退款订单表）
            Long sumSuccessRefundAmount = refundOrderDao.sumSuccessRefundAmount(payOrder.getPayOrderId());
            if (sumSuccessRefundAmount >= payOrder.getAmount()) {
                throw new BizException("退款单已完成全部订单退款，本次申请失败");
            }

            if (sumSuccessRefundAmount + rq.getRefundAmount() > payOrder.getAmount()) {
                throw new BizException("申请金额超出订单可退款余额，请检查退款金额");
            }

            String mchNo = rq.getMchNo();
            String appId = rq.getAppId();

            // 检查订单是否重复提交
            if (refundOrderService.count(new LambdaQueryWrapper<RefundOrderEntity>()
                    .eq(RefundOrderEntity::getMchNo, mchNo).eq(RefundOrderEntity::getMchRefundNo, rq.getMchRefundNo())) > 0) {
                throw new BizException("商户退款订单号[" + rq.getMchRefundNo() + "]已存在");
            }

            //获取支付参数 (缓存数据) 和 商户信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(mchNo, appId);
            if (mchAppConfigContext == null) {
                throw new BizException("获取商户配置信息失败");
            }

            MchInfoEntity mchInfo = mchAppConfigContext.getMchInfo();
            MchAppEntity mchApp = mchAppConfigContext.getMchApp();

            //获取退款接口
            IRefundService refundService =
                    SpringBeansUtil.getBean(payOrder.getIfCode() + "RefundService", IRefundService.class);
            if (refundService == null) {
                throw new BizException("当前支付渠道不支持");
            }

            refundOrder = genRefundOrder(rq, payOrder, mchInfo, mchApp);

            //退款单入库 退款单状态：生成状态  此时没有和任何上游渠道产生交互。
            refundOrderService.save(refundOrder);

            // 调用上游退款接口
            ChannelRetMsg channelRetMsg = refundService.refund(rq, refundOrder, payOrder, mchAppConfigContext);

            // 退款订单状态处理
            this.processChannelMsg(channelRetMsg, refundOrder);

            RefundOrderRS bizRes = RefundOrderRS.buildByRefundOrder(refundOrder);
            return ApiRes.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()).getAppSecret());
        } catch (BizException e) {
            return ApiRes.customFail(e.getMessage());
        } catch (ChannelException e) {
            //处理上游返回数据
            this.processChannelMsg(e.getChannelRetMsg(), refundOrder);

            if (e.getChannelRetMsg().getChannelState() == ChannelRetMsg.ChannelState.SYS_ERROR) {
                return ApiRes.customFail(e.getMessage());
            }

            RefundOrderRS bizRes = RefundOrderRS.buildByRefundOrder(refundOrder);
            return ApiRes.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()).getAppSecret());
        } catch (Exception e) {
            log.error("系统异常：{}", e);
            return ApiRes.customFail("系统异常");
        }
    }

    /**
     * 处理返回的渠道信息，并更新退款单状态
     * payOrder将对部分信息进行 赋值操作。
     **/
    private void processChannelMsg(ChannelRetMsg channelRetMsg, RefundOrderEntity refundOrder) {
        //对象为空 || 上游返回状态为空， 则无需操作
        if (channelRetMsg == null || channelRetMsg.getChannelState() == null) {
            return;
        }

        // 明确成功
        if (ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == channelRetMsg.getChannelState()) {
            this.updateInitOrderStateThrowException(RefundOrderEntity.STATE_SUCCESS, refundOrder, channelRetMsg);
            payMchNotifyService.refundOrderNotify(refundOrder);
        } else if (ChannelRetMsg.ChannelState.CONFIRM_FAIL == channelRetMsg.getChannelState()) {
            // 明确失败
            this.updateInitOrderStateThrowException(RefundOrderEntity.STATE_FAIL, refundOrder, channelRetMsg);
            payMchNotifyService.refundOrderNotify(refundOrder);
        } else if (ChannelRetMsg.ChannelState.WAITING == channelRetMsg.getChannelState() ||
                ChannelRetMsg.ChannelState.UNKNOWN == channelRetMsg.getChannelState() ||
                ChannelRetMsg.ChannelState.API_RET_ERROR == channelRetMsg.getChannelState()) {
            // 上游处理中 || 未知 || 上游接口返回异常  退款单为退款中状态
            this.updateInitOrderStateThrowException(RefundOrderEntity.STATE_ING, refundOrder, channelRetMsg);
        } else if (ChannelRetMsg.ChannelState.SYS_ERROR == channelRetMsg.getChannelState()) {
            // 系统异常：  退款单不再处理。  为： 生成状态
        } else {
            throw new BizException("ChannelState 返回异常！");
        }
    }

    /**
     * 更新退款单状态 --》 退款单生成--》 其他状态  (向外抛出异常)
     **/
    private void updateInitOrderStateThrowException(byte orderState, RefundOrderEntity refundOrder, ChannelRetMsg channelRetMsg) {
        refundOrder.setState(orderState);
        refundOrder.setChannelOrderNo(channelRetMsg.getChannelOrderId());
        refundOrder.setErrCode(channelRetMsg.getChannelErrCode());
        refundOrder.setErrMsg(channelRetMsg.getChannelErrMsg());

        boolean isSuccess = refundOrderService.updateInit2Ing(refundOrder.getRefundOrderId(), channelRetMsg.getChannelOrderId());

        if (!isSuccess) {
            throw new BizException("更定退款订单异常");
        }

        isSuccess = refundOrderService.updateIng2SuccessOrFail(refundOrder.getRefundOrderId(), refundOrder.getState(),
                channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrCode(), channelRetMsg.getChannelErrMsg());

        if (!isSuccess) {
            throw new BizException("更新退款单异常!");
        }
    }

    private RefundOrderEntity genRefundOrder(RefundOrderRQ rq, PayOrderEntity payOrder,
                                             MchInfoEntity mchInfo, MchAppEntity mchApp) {
        Date nowTime = new Date();
        RefundOrderEntity refundOrder = new RefundOrderEntity();
        refundOrder.setRefundOrderId(SeqKit.genRefundOrderId()); //退款订单号
        refundOrder.setPayOrderId(payOrder.getPayOrderId()); //支付订单号
        refundOrder.setChannelPayOrderNo(payOrder.getChannelOrderNo()); //渠道支付单号
        refundOrder.setMchNo(mchInfo.getMchNo()); //商户号
        refundOrder.setIsvNo(mchInfo.getIsvNo()); //服务商号
        refundOrder.setAppId(mchApp.getAppId()); //商户应用ID
        refundOrder.setMchName(mchInfo.getMchShortName()); //商户名称
        refundOrder.setMchType(mchInfo.getType()); //商户类型
        refundOrder.setMchRefundNo(rq.getMchRefundNo()); //商户退款单号
        refundOrder.setWayCode(payOrder.getWayCode()); //支付方式代码
        refundOrder.setIfCode(payOrder.getIfCode()); //支付接口代码
        refundOrder.setPayAmount(payOrder.getAmount()); //支付金额,单位分
        refundOrder.setRefundAmount(rq.getRefundAmount()); //退款金额,单位分
        refundOrder.setCurrency(rq.getCurrency()); //三位货币代码,人民币:cny
        refundOrder.setState(RefundOrderEntity.STATE_INIT); //退款状态:0-订单生成,1-退款中,2-退款成功,3-退款失败
        refundOrder.setClientIp(StringUtils.defaultIfEmpty(rq.getClientIp(), getClientIp())); //客户端IP
        refundOrder.setRefundReason(rq.getRefundReason()); //退款原因
        refundOrder.setChannelOrderNo(null); //渠道订单号
        refundOrder.setErrCode(null); //渠道错误码
        refundOrder.setErrMsg(null); //渠道错误描述
        refundOrder.setChannelExtra(rq.getChannelExtra()); //特定渠道发起时额外参数
        refundOrder.setNotifyUrl(rq.getNotifyUrl()); //通知地址
        refundOrder.setExtParam(rq.getExtParam()); //扩展参数
        refundOrder.setExpiredTime(DateUtil.offsetHour(nowTime, 2)); //订单超时关闭时间 默认两个小时
        refundOrder.setSuccessTime(null); //订单退款成功时间
        refundOrder.setCreatedAt(nowTime); //创建时间

        return refundOrder;
    }
}
